package untils

import (
	"bytes"
	"fmt"
	"strings"
	"time"
	"unicode/utf8"
)

type Error interface {
	error
	GetCode() int
}

type BusinessError struct {
	Err  error
	Code int
}

func (e *BusinessError) Error() string {
	return e.Err.Error()
}

func (e *BusinessError) GetCode() int {
	return e.Code
}

func NewBusinessError(code int, format string, a ...interface{}) Error {
	return &BusinessError{Err: fmt.Errorf(format, a...), Code: code}
}

func BusinessErrorf(format string, a ...interface{}) Error {
	return &BusinessError{Err: fmt.Errorf(format, a...), Code: 10000}
}

type Tree struct {
	icon        []string
	nbsp        string
	pidname     string
	childrenKey string

	data []map[string]interface{}
}

func NewTree() *Tree {
	t := &Tree{}
	t.icon = []string{"|", "├", "└"}
	t.nbsp = "&nbsp;"
	t.pidname = "pid"
	t.childrenKey = "childlist"
	return t
}

func (t *Tree) Init(data []map[string]interface{}, args ...interface{}) *Tree {
	t.data = data
	switch len(args) {
	case 1:
		t.pidname = args[0].(string)
	case 2:
		t.pidname = args[0].(string)
		t.nbsp = args[1].(string)
	}
	return t
}

// 得到子级数组
func (t *Tree) GetChild(myid interface{}) []map[string]interface{} {
	newData := []map[string]interface{}{}
	for _, value := range t.data {
		if _, ok := value["id"]; !ok {
			continue
		}

		if value[t.pidname] == myid {
			newData = append(newData, value)
		}
	}
	return newData
}

func (t *Tree) GetChildren(myid interface{}, withself ...interface{}) []map[string]interface{} {
	newData := []map[string]interface{}{}
	for _, value := range t.data {
		if _, ok := value["id"]; !ok {
			continue
		}

		if fmt.Sprintf("%v", value[t.pidname]) == fmt.Sprintf("%v", myid) {
			newData = append(newData, value)
			newData = append(newData, t.GetChildren(value["id"])...)
		} else if len(withself) > 0 && withself[0].(bool) && fmt.Sprintf("%v", value["id"]) == fmt.Sprintf("%v", myid) {
			newData = append(newData, value)
		}
	}
	return newData
}

func (t *Tree) GetChildrenIds(myid interface{}, withself ...interface{}) []interface{} {
	childrenList := t.GetChildren(myid, withself...)
	return ArrayColumn(childrenList, "id")
}

func (t *Tree) GetParents(myid interface{}, withself ...interface{}) []map[string]interface{} {
	pid := int64(0)
	newData := []map[string]interface{}{}

	for _, value := range t.data {
		if _, ok := value["id"]; !ok {
			continue
		}

		if value["id"] == myid {
			if len(withself) > 0 && withself[0].(bool) {
				newData = append(newData, value)
			}
			pid = value[t.pidname].(int64)
			break
		}
	}

	if pid > 0 {
		newData = append(newData, t.GetParents(pid, true)...)
	}

	return newData
}

func (t *Tree) GetTreeArray(myid interface{}, args ...string) []map[string]interface{} {
	itemprefix := ""
	if len(args) > 0 {
		itemprefix = args[0]
	}
	childs := t.GetChild(myid)
	total := len(childs)
	data := make([]map[string]interface{}, total)
	n := 0
	number := 1
	for _, value := range childs {
		j := ""
		k := ""
		if number == total {
			j += t.icon[2]
			if itemprefix != "" {
				k = t.nbsp
			}
		} else {
			j += t.icon[1]
			if itemprefix != "" {
				k = t.icon[0]
			}
		}
		spacer := ""
		if itemprefix != "" {
			spacer = itemprefix + j
		}

		value["spacer"] = spacer
		data[n] = value
		data[n][t.childrenKey] = t.GetTreeArray(value["id"], itemprefix+k+t.nbsp)
		n++
		number++
	}

	return data
}

func (t *Tree) GetTreeList(data []map[string]interface{}, args ...string) []map[string]interface{} {
	field := "name"
	if len(args) > 0 {
		field = args[0]
	}
	arr := []map[string]interface{}{}
	for _, v := range data {
		childList := []map[string]interface{}{}
		if _, ok := v["childlist"]; ok {
			childList = v["childlist"].([]map[string]interface{})
		}
		delete(v, "childlist")
		v[field] = v["spacer"].(string) + " " + v[field].(string)
		v["haschild"] = 0
		if len(childList) > 0 {
			v["haschild"] = 1
		}

		if v["id"].(int64) > 0 {
			arr = append(arr, v)
		}

		if len(childList) > 0 {
			arr = append(arr, t.GetTreeList(childList, field)...)
		}
	}
	return arr
}

func ArrayColumn(d []map[string]interface{}, columnKey string) []interface{} {
	nd := []interface{}{}
	for _, v := range d {
		nd = append(nd, v[columnKey])
	}
	return nd
}

func ArrayColumnIndex(d []map[string]interface{}, columnKey string, indexKey string) map[string]interface{} {
	nd := make(map[string]interface{})
	for _, v := range d {
		k := fmt.Sprintf("%v", v[indexKey])
		nd[k] = v[columnKey]
	}
	return nd
}

func InArray(needle interface{}, haystack []interface{}) bool {
	for _, v := range haystack {
		if fmt.Sprintf("%v", needle) == fmt.Sprintf("%v", v) {
			return true
		}
	}
	return false
}

func ArrayKeysString(elements interface{}) []interface{} {
	elems := elements.(map[string]interface{})
	keys := make([]interface{}, len(elems))
	for key := range elems {
		keys = append(keys, key)
	}
	return keys
}

func ArrayDiff(arr1 []interface{}, arrs ...[]interface{}) (data []interface{}) {
	if len(arrs) == 0 {
		return arr1
	}
	i := 0
loop:
	for {
		if i == len(arr1) {
			break
		}
		v := arr1[i]
		for _, arr := range arrs {
			for _, val := range arr {
				if fmt.Sprintf("%v", v) == fmt.Sprintf("%v", val) {
					i++
					continue loop
				}
			}
		}
		data = append(data, v)
		i++
	}
	return

}

func ArrayFilter(haystack []interface{}, callback func(val interface{}) bool) []interface{} {
	nhaystack := []interface{}{}
	for _, v := range haystack {
		if callback(v) {
			nhaystack = append(nhaystack, v)
		}
	}
	return nhaystack
}

func ArrayIntersect(array1, array2 []interface{}) (array []interface{}) {
	hash := map[interface{}]bool{}
	for _, v := range array1 {
		hash[v] = true
	}

	for _, v := range array2 {
		if hash[v] {
			array = append(array, v)
		}
	}

	return
}

func Implode(glue string, pieces []string) string {
	var buf bytes.Buffer
	l := len(pieces)
	for _, str := range pieces {
		buf.WriteString(str)
		if l--; l > 0 {
			buf.WriteString(glue)
		}
	}
	return buf.String()
}

func DateLayout(layout string) string {
	layout = strings.Replace(layout, "Y", "2006", -1)
	layout = strings.Replace(layout, "m", "01", -1)
	layout = strings.Replace(layout, "d", "02", -1)
	layout = strings.Replace(layout, "H", "15", -1)
	layout = strings.Replace(layout, "i", "04", -1)
	return strings.Replace(layout, "s", "05", -1)
}

func Date(layout string) string {
	return time.Now().Format(DateLayout(layout))
}

func MbStrLeg(s string) int {
	return utf8.RuneCountInString(s)
}

func MbSubStr(s string, start int, a ...int) string {

	if s == "" {
		return s
	}

	var p, length int

	mbtab := []int{}
	for p = range s {
		mbtab = append(mbtab, p)
	}

	mbtablen := len(mbtab)

	if start < 0 {
		start = mbtablen + start
	}
	if start < 0 {
		start = 0
	}
	if len(a) > 0 {
		length = a[0]
	} else {
		length = mbtablen
	}

	if length < 0 {
		length = mbtablen - start + length
	}

	if length < 0 {
		return ""
	}

	if start > mbtablen {
		return ""
	}

	if length+start >= mbtablen {
		return s[mbtab[start]:]
	} else {
		return s[mbtab[start]:mbtab[length+start]]
	}
}
